// 防抖
function  deBounce(fn,delay=2000) {
    let timer = null;
    return function(...args) {
        if (timer) {
            clearTimeout(timer)
            timer = null;
        }
        timer = setTimeout(()=>{
            fn.call(this,...args)
        },delay)
    }
}
// 时间戳
function  throttle(fn,delay=500) {
    let  pre = 0;
    return function(...args) {
        let currentDate = new Date()
        if (currentDate - pre > delay) {
            fn.call(this,...args)
            pre  =  now;
        }
    }
}
// 定时器
function  throttle1(fn,delay=500) {
    let timer = null;
    return function (...args) {
        if (!timer) {
            timer = setTimeout(()=>{
                fn.call(this,...args)
                clearTimeout(timer)
                timer = null
            },delay)
        }
    }
}

// const newCopyObj = JSON.parse(JSON.stringify(oldObj))
let objaaa = {
    a : 'zhangsan',
    b : 'lisi',
    c : {
        d:3,
        e:4
    },
    f : [1,2,3,4]
}
function deepCopy(obj){
    let result = null;
    if (obj && typeof obj == 'object') {
        result = obj.constructor == Array ? [] : {}
        for (const key in obj) {
            result[key] = (typeof obj[key] == 'object') ? deepCopy(obj[key]) : obj[key]
        }
    }else{
        result = obj
    }
    return result;
}
// eventBus
class EventBus{
    constructor(){
        this._events = this._events || new Map()
    }
}
EventBus.prototype.emit = function(type,...args){
    let handle = this._events.get(type)
    if (args.length > 0) {
        handle.call(this,...args)
    }else{
        handle.call(this)
    }
    return true
}
EventBus.prototype.addListener = function(type,fn){
   if (!this._events.has(type)) {
        this._events.set(type,fn)
   }
}
function Student(){
    this.name = 'zhangsan'
}
function Person(){
    this.name = 'zhangsan'
}
// let student = new Student();
// console.log(student instanceof Student);
function instance_of(left,right){
    let proto = left.__proto__;
    let prototype = right.prototype;
    while (true) {
        if(proto == null) return false;
        if (proto == prototype) return true;
        proto = proto.__proto__;
    }
}
// console.log(instance_of(student,Student));
function creatNew(Cla,...args){
    let obj = {};
    obj.__proto__ = Cla.prototype;
    Cla.apply(obj,args)
    return obj
}
let student = creatNew(Student)
// console.log(student instanceof Student);
Function.prototype._call = function(context){
    if (context == null || context == undefined) {
        context = window;
    }
    context._fn = this;
    let args = [];
    for (let index = 1; index < arguments.length; index++) {
        args.push(arguments[i])
    }
    let result = context._fn(...args);
    delete context._fn;
    return result;
}


function Teacher(){
    this.sex = '女'
    Person._call(this)
}
// let teacher = new Teacher();
// console.log(teacher.name);
// new 会把内部的this指向 外部的实例
// create 只是构造了一个新的函数 然后新的函数的prototype指向了 某一个构造函数的原型 没法访问this中的实例属性
let teacher1 = Object.create(Teacher.prototype);
function objCreate(prototype){
    function Fun(){};
    Fun.prototype = prototype;
    return new Fun();
}
// 继承
function Person1(){
    this.name = 'zhangsan';
}
Person1.prototype.sayHi = function(){
    console.log('---sayHi---');
}
function Teacher1() {
    this.age = 18;
    Person1.call(this)
}
Teacher1.prototype = Object.create(Person1.prototype);
Teacher1.prototype.constructor = Teacher1
let teacher2 = new Teacher1();
// teacher2.sayHi()
// let  teacher3 = new Teacher1();
// teacher3.name = 'lisi'
// console.log(teacher3.name);
// console.log(teacher2.name);
// console.log(teacher2.constructor === Teacher1);
// promise
function myPromise(constructor){
    this.value = undefined;
    this.reason = undefined;
    this.status = 'pending';
    let self = this;
    function resolve(val){
        if (self.status == 'pending') {
            self.value = val;
            self.status = 'resolved'
        }
    }
    function reject(reason){
        if (self.status == 'pending') {
            self.reason = reason;
            self.status = 'rejected'
        }
    }
    try {
        constructor(resolve,reject)
    } catch (error) {
        reject(error)
    }
}
myPromise.prototype.then = function(onFullfilled,onRejected){
    let self = this;
    switch (self.status) {
        case 'resolved':
            onFullfilled(self.value)
            break;
        case 'resolved':
            onRejected(self.reason)
            break;
        default:
            break;
    }
}
// let p = new myPromise(((resolve,reject)=>{
//     resolve(1);
// }))
// p.then((result)=>{
//     console.log('---result---',result);
// })

// Array
// push/pop/shift/unshift/sort/reverse/concat/slice/splice/indexOf
// arr.sort() 不加函数的话是对字符串排序
// arr.sort((a,b)=>a-b) //对大小排序
// arr.splice() //第一个参数表示开始的下标 第二个表示要删除的长度 后面的参数表示要插入的数据
// arr.join() //数组转换成字符串默认用逗号隔开
// indexOf元素对应的下标
// lastIndexOf 元素对应的下标 从末尾开始查找

// forEach/
// forEach没有返回值
// map有返回值，返回计算的结果组成的数组
// filter 返回符合条件的元素组成的数组
// every 判断数组中是否每一项都满足条件 都满足返回true
// some 有满足的元素就返回true

let oldArr = [1,5,9,4,3,7,5,3,1,5,7,8];
// let arr1 = oldArr.map((item,index)=>{
//     return item > 5
// })
 // 实现一个 map方法 通过Array.prototype
Array.prototype._map = function(fn,context = null){
    let arr = []
    if (typeof fn == 'function') {
       for (let i = 0; i < this.length; i++) {
            arr.push(fn.call(context,this[i],i))
       }
        // 方式二
        // this.reduce(function(pre,cur,index){
        //     arr.push(fn.call(context,cur,index))
        // },0)
    }
    return arr;
}
// let newArr =  oldArr._map((item,index)=>{
//     return item + 5
// },this)
// console.log(newArr);

// 找出字符串中连续出现最多的字符和个数
let str = "abcabcabbbbbbbcbbcccccb";
function findStr(str){
    let mapObj = new Map();
    for (let i = 0; i < str.length; i++) {
        if (!mapObj.has(str[i])) {
           mapObj.set(str[i],1) 
        }else{
           let num = mapObj.get(str[i])
           mapObj.set(str[i],num+1)
        }
    }
    console.log(mapObj);
    let maxNum = 0
    let maxKey = 0
    for ( const [ key, value ] of mapObj) {
        if (value > maxNum) {
            maxNum = value;
            maxKey = key
        }
    }
    console.log(maxKey,maxNum);
}
// findStr(str)

// async function async1() {
//     console.log('async1 start') 
//     await async2() 
//     console.log('async1 end')
// }
// async function async2() {
//     console.log('async2')
// }
// console.log('script start') 
// setTimeout(function () {
//     console.log('setTimeout')
// }, 0) 
// async1() 
// new Promise(function (resolve) {
//     console.log('promise1') 
//     resolve()
// }).then(function () {
//     console.log('promise2')
// }) 
// console.log('script end')

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout
// 已知如下数组，编写一个程序将数组扁平化去并除其中重复部分数据，最终得 到一个升序且不重复的数组 
let arr3 = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
function InfinityStr(arr){
   let newarr = arr.join().split(',')
   let arrIn = Array.from(new Set(newarr)).sort((a,b)=> a-b)
   console.log(arrIn);
}
// 递归
function Infinity1(arr){
   let newArr = [];
   arr.map(item=>{
      return (typeof item == 'object') ? newArr = newArr.concat(Infinity1(item)) : newArr.push(item)
   })
   console.log(newArr);
   return newArr
}
// flat    arr.flat(Infinity)可以将多维数组转换成一维数组
function InfinityFlat(arr){
   return Array.from(new Set(arr.flat(Infinity))).sort((a,b)=> a-b)
}
// console.log(InfinityFlat(arr3));

function test () { 
    console.log('start') 
    setTimeout(() => { //算是一个宏任务
        console.log('children2') 
        Promise.resolve().then(() => {
            console.log('children2-1')
        }) 
    }, 0) 
    setTimeout(() => { 
        console.log('children3') 
        Promise.resolve().then(() => {
            console.log('children3-1')
        })
    }, 0) 
    Promise.resolve().then(() => {
        console.log('children1')
    }) 
    console.log('end') 
}
// test()
// start
// end
// children1
// children2
// children2-1
// children3
// children3-1
// 请把两个数组 ['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'] 和 ['A', 'B', 'C', 'D']，合并 为
// ['A1', 'A2', 'A', 'B1', 'B2', 'B', 'C1', 'C2', 'C', 'D1', 'D2', 'D']。
function concatArr(arr1,arr2){
    let current = 0
    let arr = [...arr1]
    for (let i = 0; i < arr2.length; i++) {
        while (current < arr.length) {
            ++current;
            if (current == arr.length || arr[current][0] !== arr2[i]) {
                arr.splice(current,0,arr2[i])
                break;
            }
        }
    }
    console.log(arr);
}
// concatArr(['A1', 'A2', 'B1', 'B2', 'C1', 'C2', 'D1', 'D2'],['A', 'B', 'C', 'D'])
// 改造下面的写法 顺序输入0-9
function exp(){
    for (let i = 0; i < 10; i++) {
        setTimeout(()=>{
            console.log(i);
        },1000)
    }
}
//1
function exp1(){
    for (let i = 0; i< 10; i++){ 
        setTimeout((index) => { 
            console.log(index); 
        }, 1000,i)
    } 
}
// exp2()
//2
function exp2(){
    for (var i = 0; i< 10; i++){ 
        ((index) => { 
            setTimeout(() => { 
                console.log(index);
            }, 1000) 
        })(i)
    }
}

// var b = 10;
// (function b(){ //js作用域中先找最近的声明 
//     b = 20
//     console.log(b); //打印的是b的函数 
// })()
// console.log(b); //10
function ti34(){
    // 改造代码 分别打印10和20
    // 函数声明优先级比变量高
    var b = 10;
    (function b(){
        b = 20
        console.log(b);
    })()

    // (function (){
    //     b = 20
    //     console.log(b); //20
    // })()
    // (function (){
    //     console.log(b); //10
    //     b = 20
    // })()
}
// 下面代码中 a 在什么情况下会打印 1？
function ti38(){
    var a = { 
        i: 1, 
        toString() { //类型的隐式转换 会调用tostring 先返回a.i 在执行++
            return a.i++; 
        }
    }
    if( a == 1 && a == 2 && a == 3 ) { 
        console.log(1); 
    }
}
// ti38()
// let a = [1,2,3]; 
// a.toString = a.shift;
// if( a == 1 && a == 2 && a == 3 ) { 
//     console.log(1);
// }



let arr = [14,94,86,4,36,47,825,69,42,36,15,48,16,17]
function sortPao(arr){
    let isOk = true
    for (let i = 0; i < arr.length; i++) {
        for (let j = 0; j < arr.length - i - 1; j++) {
            let temp;
            if (arr[j] > arr[j+1]) {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                isOk = false
            }
        }
        if (isOk) break
    }
    
    console.log(arr);
}
// sortPao(arr);
// ti55
// {1:222, 2:123, 5:888}，请把数据处理为如下结构：[222, 123, null, null, 888, null, null, null, null, null, null, null]。
let obj = {1:222, 2:123, 5:888}; 
function objToArr(obj){
    Array.from({length:12}).map((_,index)=>{
        return obj[index+1] || null;
    })
}


class LazyManClass{
    constructor(name){
        this.name = name;
        console.log(` Hi I am ${this.name}`);
        this.queue = []
        setTimeout(()=>{
            this.next()
        },0)
    }
    sleepFirst(time){
        const fn = () =>{
            setTimeout(()=>{
                console.log(`等待了${time}秒...`);
                this.next();
            },time*1000)
        }
        this.queue.unshift(fn)
        return this 
    }
    sleep(time) { 
        const fn = () => { 
            setTimeout(() => { 
                console.log(`等待了${time}秒...`) 
                this.next() 
            },time*1000) 
        }
            this.queue.push(fn) 
            return this 
    }
    eat(food) { 
        const fn = () => { 
            console.log(`I am eating ${food}`) 
            this.next() 
        }
            this.queue.push(fn) 
            return this 
    }
    next() { 
        const fn = this.queue.shift() 
        fn && fn() 
    }
}
function LazyMan(name) { 
    return new LazyManClass(name)
}
// LazyMan('Tony'); // Hi I am Tony 
// LazyMan('Tony').sleep(10).eat('lunch'); // Hi I am Tony // 等待了 10 秒... // I am eating 
// LazyMan('Tony').eat('lunch').sleep(10).eat('dinner'); // Hi I am Tony // I am eating lunch// 等待了 10 秒... // I am eating 
// LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food'); // Hi I am Tony// 等待了 5 秒... // I am eating lunch // I am eating dinner // 等待了 10 秒... // I am eating junk food
// LazyMan('Tony').eat('lunch').eat('dinner').sleepFirst(5).sleep(10).eat('junk food')
let arr1 = [1,5,9,7,3,2] 
let arr2 = [1,8,6,7,3,4]
function ti59(arr1,arr2){
    return arr1.filter(item=>{
        return arr2.indexOf(item) > -1
    })
//    return arr1.filter(item=>{
//         return arr2.includes(item)
//     })
}
// console.log(ti59(arr1,arr2));
Promise.prototype._finally = function(callback){
    return this.then(res=>{
        return Promise.resolve(callback()).then(()=>res)
    },err=>{
        return Promise.resolve(callback()).then(()=>{
            throw err
        })
    })
}
Promise.prototype._all = function(arr=[]){
    return new Promise((resolve,reject)=>{
        arr = Array.from(arr)
        if (arr.length == 0) {
            resolve([])
        }
        let result = []
        let count = 0
        for (let i = 0; i < arr.length; i++) {
           Promise.resolve(arr[i]).then(data=>{
             result[i] = data;
             count++;
             if (count == arr.length) {
                resolve(result)
             }
           },err=>{
             reject(err)
           })
        }
    })
}
Promise.prototype._race = function(arr = []){
    return new Promise((resolve,reject)=>{
        arr = Array.from(arr)
        if (arr.length == 0) {
            resolve([])
        }else{
            for (let i = 0; i < arr.length; i++) {
                Promise.resolve(arr[i]).then(data=>{
                    resolve(data)
                    return
                },err=>{
                    reject(err)
                    return
                })
            }
        }
    })
}
// ES6 转 ES5思路
// ES6 转 ES5 目前行业标配是用 Babel，转换的大致流程如下： 
// 1. 解析：解析代码字符串，生成 AST； 
// 2. 转换：按一定的规则转换、修改 AST； 
// 3. 生成：将修改后的 AST 转换成普通代码。 如果不用工具，纯人工的话，就是使用或自己写各种 polyfill 了。




// 随机生成一个长度为 10 的整数类型的数组，例如 [2, 10, 3, 4, 5, 11, 10, 11, 20]，将其排列成一个新数组，要求新数组形式如下，例如 [[2, 3, 4, 5], [10, 11], [20]]。
function ti67(arr = []){
    if (!Array.isArray(arr) || arr.length <= 0) return []
    let newArr =  Array.from(new Set(arr)).sort((a,b)=>a-b);
    let first = 0;
    let sec = 0
    let result = []
    let preV = null;
    while (sec < newArr.length) {
        prev = newArr[sec]
        sec++
        if (sec == newArr.length || newArr[sec] - prev != 1) {
            let tempArr = newArr.slice(first,sec)
            result.push(tempArr);
            first = sec;
        }
    }
    console.log(result);
}
// ti67([1, 10, 3, 4, 5, 11, 10, 11, 20])
function ti69(str){
    let arr = str.split('')
    console.log(arr);
   let arr1 = arr.map(item=>{
        return item == item.toUpperCase() ? item.toLowerCase() : item.toUpperCase()
    })
    console.log(arr1.join(''));
}
// ti69('aBc')
// 实现一个字符串匹配算法，从长度为 n 的字符串 S 中，查找是否存在字符串 T，T 的长度是 m，若存在返回所在位置
let nStr = 'dasfsagfgarenbgfytjvxc' 
let tStr = 'fgar'
function ti71(str1,str2){
    for (let i = 0; i < str1.length; i++) {
        if (str1[i] == str2[0]) {
            if (str1.substr(i,str2.length) == str2) {
                return i
            }
        }
    }
    return -1
}
// console.log(ti71(nStr,tStr));
// example 1 

// var a={}, b='123', c=123; 
// a[b]='b'; //{'123':'b'}
// a[c]='c'; //{123:'c'}
// console.log(a[b]);// c // 对象的键名只能是字符串和 Symbol 类型。 2. 其他类型的键名会被转换成字符串类型。 3. 对象转字符串默认会调用 toString 方法。
// example 2
// var a={}, b=Symbol('123'), c=Symbol('123'); Symbol会产生唯一的值
// a[b]='b'; 
// a[c]='c'; 
// console.log(a[b]); //b
// example 3
// var a={}, b={key:'123'}, c={key:'456'}; //调用了tostring() 转换成了[object Object]
// a[b]='b';
// a[c]='c'; 
// console.log(a[b]);//c

// ti100
function Foo() { 
    Foo.a = function() { 
        console.log(1); 
    };
    this.a = function() { 
        console.log(2); 
    }; 
}
Foo.prototype.a = function() { 
    console.log(3); 
};
Foo.a = function() { 
    console.log(4);
}
// Foo.a(); 
// let obj1 = new Foo(); 
// obj1.a(); 
// Foo.a();

// ti84
// add(1); // 1add(1)(2); // 3add(1)(2)(3)； // 6add(1)(2, 3); // 6add(1, 2)(3); // 6add(1, 2, 3);
function currying(fn) { 
    length = fn.length; //  第一次调用获取函数 fn 参数的长度，后续调用获取 fn 剩余参数的 长度
     return function (...args) { // currying 包裹之后返回一个新函数，接收参数为 ...args
        return args.length >= length ? //新函数接收的参数长度是否大于等于 fn 剩余参数需要接收的长度
        fn.apply(this, args) : //满足要求，执行 fn 函数，传入新函数的参数
        currying(fn.bind(this, ...args), length - args.length) //不满足要求，递归 currying 函数，新的 fn 为 bind 返回的新函数（bind 绑定了 ...args 参数，未执行），新的 length 为 fn 剩余参数的长度
    }
}

function add(...args) {
	return args.reduce((a, b) => a + b)
}
// let sum =  currying(add);
// console.log(sum(1)(2)());

// // 第二种
// function add2() {
//     let args = [].slice.call(arguments);
//     let fn = function(){
//      let fn_args = [].slice.call(arguments)
//      return add2.apply(null,args.concat(fn_args))
//      }
//     fn.toString = function(){
//         return args.reduce((a,b)=>a+b)
//     }
//     return fn
//   }
//   console.log(add2(1)(2)(3));